package com.lgcns.weather;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;

import com.lgcns.weather.anim.Rotate3dAnimation;
import com.lgcns.weather.db.QueryHelper;
import com.lgcns.weather.info.CurrentInfo;
import com.lgcns.weather.info.LocationInfo;
import com.lgcns.weather.info.ShortRangeInfo;
import com.lgcns.weather.info.WeeklyInfo;
import com.lgcns.weather.util.InflatedViewHolder;
import com.lgcns.weather.util.Util;

/**
 * 날씨 정보를 보여주기 위한 메인 Activity
 */
public class HurryWeatherActivity extends Activity implements OnClickListener, SensorEventListener {
    private static final String TAG = "HurryWeatherActivity";

    /** 환경설정에 저장된 locationId */
    private int favoriteLocationId = -1;
    /** 관심지역 환경설정화면에서 locationId가 변경되었는지 여부 */
    private boolean locationIdChanged = false;
    /** 기상정보를 이미 수신한 적이 있는지 여부 */
    private boolean alreadyWeatherInfoReceived = false;
    /** 관심지역 기상코드 정보 */
    LocationInfo locationInfo;

    /** 현재 날씨 */
    private CurrentInfo currentInfo;
    /** 단기 예보 */
    private ShortRangeInfo shortInfo;
    /** 주간 예보 */
    private WeeklyInfo weeklyInfo;

    /** 기상정보 수신시 보여줄 ProgressDialog */
    private ProgressDialog progress;

    /** 메시지 : 파싱 에러 */
    private static final int MSG_PARSING_ERROR = 0;
    /** 메시지 : 파싱 중 */
    private static final int MSG_PARSING_PROGRESS = 1;
    /** 메시지 : 파싱 완료 */
    private static final int MSG_PARSING_COMPLETED = 2;
    /** 메시지 : 위성사진 수신 에러 */
    private static final int MSG_SAT_IMG_PARSING_ERROR = 3;
    /** 메시지 : 위성사진 수신 완료 */
    private static final int MSG_SAT_IMG_PARSING_COMPLETED = 4;

    /** 환경설정 : DB 파일 복사 여부 */
    public static final String PREF_DB_FILE_COPY = "db_file_copy";
    /** 환경설정 : 관심지역 ID */
    public static final String PREF_FAVORITE_LOCATION_ID = "favorite_location";

    /** request code : 설치시 관심지역 선택 Activity 호출 */
    private static final int REQ_FIRST_FAVORITE_ACTIVITY = 1;
    /** request code : 관심지역 설정 Activity 호출 */
    private static final int REQ_FAVORITE_ACTIVITY = 2;

    /** 3D Rotation 애니메이션 동작 시간 */
    private static final int ROTATION_DURATION = 500;

    /** 오늘/주간 영역을 가지고 있는 FrameLayout */
    FrameLayout frameLayout;
    /** 오늘/단기 영역 */
    LinearLayout todayPage;
    /** 주간 영역 */
    LinearLayout weeklyPage;
    /** 위성사진 영역 */
    LinearLayout satPage;
    /** 단기예보에서 inflate된 view들을 보관하는 holder */
    InflatedViewHolder shortViewHolder;
    /** 주간예보에서 inflate된 view들을 보관하는 holder */
    InflatedViewHolder weeklyViewHolder;

    /* 현재날씨 View에 대한 전역 변수 */
    TextView nowLocationName;
    TextView nowTime;
    TextView nowTemp;
    TextView nowWeather;
    TextView nowRainFall;
    ImageView nowIcon;
    TableRow shortDateRow;
    TableRow shortDayRow;
    TableRow shortHourRow;
    TableRow shortWeatherImageRow;
    TableRow shortWeatherRow;
    TableRow shortTempRow;
    TableRow shortRainRateRow;
    TableRow shortRainFallRow;
    TableRow shortWindSpeedRow;
    TableRow shortHumidityRow;

    /* 주간날씨 View에 대한 전역 변수 */
    TableLayout weeklyTable;
    TextView weeklyLocationName;
    TextView weeklyTime;
    TextView weeklyExplain;
    TableRow weeklyDayRow;
    TableRow weeklyDateRow;
    TableRow weeklyWeatherImageRow;
    TableRow weeklyWeatherRow;
    TableRow weeklyTempMaxRow;
    TableRow weeklyTempMinRow;

    /* 위성사진 */
    ImageView satImage;
    Drawable satImageDrawable;
    TextView satImageTime;
    String satImageTimeString;
    
    /* 센서 */
    private SensorManager sensorManager;
    private Sensor accelerometer; // 가속도 센서
    

    /** 날씨 아이콘 목록 정보 */
    HashMap<String, Integer> weatherIconList;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Log.i(TAG, "onCreate()");

        frameLayout = (FrameLayout) findViewById(R.id.weather_frame);
        todayPage = (LinearLayout) findViewById(R.id.today_page);
        weeklyPage = (LinearLayout) findViewById(R.id.weekly_page);
        satPage = (LinearLayout) findViewById(R.id.sat_page);

        findViewById(R.id.show_today_btn).setOnClickListener(this);
        findViewById(R.id.show_weekly_btn).setOnClickListener(this);
        findViewById(R.id.show_sat_btn).setOnClickListener(this);
        findViewById(R.id.select_favorite_btn).setOnClickListener(this);
        findViewById(R.id.receive_info_btn).setOnClickListener(this);

        // DB 파일 복사 여부
        boolean dbFileCopy = Util.getPreferenceBooleanValue(this, PREF_DB_FILE_COPY, false);

        if (!dbFileCopy) {
            copyDBFile();
            Util.savePreference(this, PREF_DB_FILE_COPY, true);
        }

        shortViewHolder = new InflatedViewHolder();
        weeklyViewHolder = new InflatedViewHolder();

        initWeatherIconList();

        // Since we are caching large views, we want to keep their cache
        // between each animation
        frameLayout.setPersistentDrawingCache(ViewGroup.PERSISTENT_ANIMATION_CACHE);
        
        sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
        List<Sensor> list = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
        if(list.size() > 0) {
            accelerometer = list.get(0);
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i(TAG, "onStart()");

        // 관심지역 ID (저장된 적이 없으면 -1로 세팅)
        favoriteLocationId = Util.getPreferenceIntValue(this, PREF_FAVORITE_LOCATION_ID, -1);

        if (favoriteLocationId == -1) {
            new AlertDialog.Builder(this).setTitle(R.string.new_favorite_dialog_title)
                    .setMessage(R.string.new_favorite_dialog_message)
                    .setPositiveButton(R.string.ok_button_text, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                            startActivityForResult(new Intent(getApplicationContext(),
                                    FavoriteLocationActivity.class), REQ_FIRST_FAVORITE_ACTIVITY);
                        }
                    }).show();
            // 기상정보를 수신한 적이 없거나 환경설정에서 관심지역이 변경된 경우만 기상정보 새로 수신.
        } else if (!alreadyWeatherInfoReceived || locationIdChanged) {
            receiveWeatherInfo(favoriteLocationId);
            // 위성사진 정보를 수신한다.
            receiveSatPicture();
        } else {
            // 최초 실행된 것이 아니거나, 관심지역 설정 화면에서 그냥 돌아온 경우는 화면에 기존에 수신한 날씨 표시
            showWeatherInfo();
        }
    }
    
    @Override
    protected void onResume() {
        super.onResume();
        
        if(accelerometer != null) {
            sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI);
        }
    }

    // 버튼 처리
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.show_today_btn:
                if (todayPage.getVisibility() != View.VISIBLE) {
                    // applyRotation(-1, 180, 90); 원래 ApiDemos 소스
                    applyRotation(0, 0, -90);
                }
                break;
            case R.id.show_weekly_btn:
                if (weeklyPage.getVisibility() != View.VISIBLE) {
                    // 회전 방향 결정(가운데 화면만 판단하면 됨)
                    if (todayPage.getVisibility() == View.VISIBLE) {
                        applyRotation(1, 0, 90);
                    } else {
                        applyRotation(1, 0, -90);
                    }
                }
                break;
            case R.id.show_sat_btn:
                if (satPage.getVisibility() != View.VISIBLE) {
                    applyRotation(2, 0, 90);
                }
                break;
            case R.id.select_favorite_btn:
                startActivityForResult(new Intent(getApplicationContext(), FavoriteLocationActivity.class),
                        REQ_FAVORITE_ACTIVITY);
                break;
            case R.id.receive_info_btn:
                receiveWeatherInfo(favoriteLocationId);
                break;
        }
    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.i(TAG, "onActivityResult()");

        switch (requestCode) {
            // 설치시 관심지역 선택 Activity를 호출하여 result 받은 경우
            case REQ_FIRST_FAVORITE_ACTIVITY:
                if (resultCode == RESULT_OK) {
                    // 선택한 경우
                    favoriteLocationId = data.getIntExtra(PREF_FAVORITE_LOCATION_ID, -1);
                } else {
                    // 뒤로가기로 돌아온 경우 1로 기본값 세팅
                    favoriteLocationId = 1;
                }
                locationIdChanged = true;
                Util.savePreference(this, PREF_FAVORITE_LOCATION_ID, favoriteLocationId);
                break;

            // 메뉴 등에서 관심지역 선택 Activity를 호출하여 result 받은 경우
            case REQ_FAVORITE_ACTIVITY:
                if (resultCode == RESULT_OK) {
                    // 선택한 경우
                    int locationId = data.getIntExtra(PREF_FAVORITE_LOCATION_ID, -1);
                    locationIdChanged = (locationId != favoriteLocationId);
                    if (locationIdChanged) {
                        // 관심지역이 변경된 경우에만 환경설정에 저장
                        favoriteLocationId = locationId;
                        Util.savePreference(this, PREF_FAVORITE_LOCATION_ID, favoriteLocationId);
                    }
                } else {
                    // 뒤로가기로 돌아온 경우
                    locationIdChanged = false;
                }

                break;
        }
    }
    
    public void onSensorChanged(SensorEvent event) {
        if(event.sensor == accelerometer) {
            //values[0]: Acceleration minus Gx on the x-axis 
            //values[1]: Acceleration minus Gy on the y-axis 
            //values[2]: Acceleration minus Gz on the z-axis 
            float x = event.values[0];
            float y = event.values[1];
            float z = event.values[2];
            
            if(x  >= 15 || y >= 15 || z >= 15) {
                receiveWeatherInfo(favoriteLocationId);
            }
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }

    /** Thread로 부터의 메시지를 처리할 Handler */
    Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            Log.i(TAG, "handleMessage()");
            switch (msg.what) {
                case MSG_PARSING_PROGRESS:
                    // 진척률 증가
                    progress.incrementProgressBy(20);
                    break;
                case MSG_PARSING_COMPLETED:
                    // 파싱 완료
                    progress.dismiss();
                    progress = null;
                    showWeatherInfo();
                    break;
                case MSG_SAT_IMG_PARSING_COMPLETED:
                    // 위성사진 다운로드 완료
                    showSatImageInfo();
                    break;
                case MSG_PARSING_ERROR:
                    // 파싱 에러
                    progress.dismiss();
                    progress = null;
                    Toast.makeText(getApplicationContext(), R.string.parsing_error_message,
                            Toast.LENGTH_SHORT).show();
                    // 일단 Activity를 종료.
                    HurryWeatherActivity.this.finish();
                    break;
            }
        };
    };

    /**
     * 기상 정보를 화면에 출력한다.
     */
    private void showWeatherInfo() {
        showCurrentWeather();
        showWeeklyWeather();
    }

    /**
     * 현재 날씨 영역을 표시한다.
     */
    private void showCurrentWeather() {
        nowLocationName = (TextView) findViewById(R.id.now_location_name);
        nowLocationName.setText(locationInfo.getLocation());
        nowTime = (TextView) findViewById(R.id.now_time);
        nowTime.setText(currentInfo.getUpdateDate() + " " + getResources().getString(R.string.now));
        nowTemp = (TextView) findViewById(R.id.now_temp);
        nowTemp.setText(currentInfo.getTemperature() + getResources().getString(R.string.celsius));
        nowWeather = (TextView) findViewById(R.id.now_weather);
        nowWeather.setText(currentInfo.getDescription());
        nowRainFall = (TextView) findViewById(R.id.now_rainfall);
        String rainFall = currentInfo.getRainFall();
        if (rainFall != null && !rainFall.equals("0")) {
            nowRainFall.setText(String.format(getResources().getString(R.string.now_rain_fall),
                    currentInfo.getRainFall()));
        }
        nowIcon = (ImageView) findViewById(R.id.now_icon);
        nowIcon.setImageResource(getWeatherIcon(currentInfo.getIcon()));

        LayoutInflater inflater = getLayoutInflater();

        shortDateRow = (TableRow) findViewById(R.id.short_date_row);
        shortDayRow = (TableRow) findViewById(R.id.short_day_row);
        shortHourRow = (TableRow) findViewById(R.id.short_hour_row);
        shortWeatherImageRow = (TableRow) findViewById(R.id.short_weather_image_row);
        shortWeatherRow = (TableRow) findViewById(R.id.short_weather_row);
        shortTempRow = (TableRow) findViewById(R.id.short_temp_row);
        shortRainRateRow = (TableRow) findViewById(R.id.short_rainrate_row);
        shortRainFallRow = (TableRow) findViewById(R.id.short_rainfall_row);
        shortWindSpeedRow = (TableRow) findViewById(R.id.short_windspeed_row);
        shortHumidityRow = (TableRow) findViewById(R.id.short_humidity_row);

        shortViewHolder.removeAll();

        int length = shortInfo.getDataLength();
        for (int i = 0; i < length; i++) {
            TextView shortDate = (TextView) inflater.inflate(R.layout.short_row_date, null);
            shortDate.setText(shortInfo.getDate(i));
            shortDate.setGravity(Gravity.CENTER);
            shortDateRow.addView(shortDate);
            shortViewHolder.add(shortDateRow, shortDate);

            TextView shortDay = (TextView) inflater.inflate(R.layout.short_row_day, null);
            shortDay.setText(shortInfo.getDayOfWeek(i));
            shortDay.setGravity(Gravity.CENTER);
            shortDayRow.addView(shortDay);
            shortViewHolder.add(shortDayRow, shortDay);

            TextView shortHour = (TextView) inflater.inflate(R.layout.short_row_hour, null);
            shortHour.setText(shortInfo.getHour(i));
            shortHour.setGravity(Gravity.CENTER);
            shortHourRow.addView(shortHour);
            shortViewHolder.add(shortHourRow, shortHour);

            ImageView shortWeatherImage = (ImageView) inflater
                    .inflate(R.layout.short_row_weather_image, null);
            shortWeatherImage.setImageResource(getWeatherIcon(shortInfo.getWeatherCode(i)));
            shortWeatherImageRow.addView(shortWeatherImage);
            shortViewHolder.add(shortWeatherImageRow, shortWeatherImage);

            TextView shortWeather = (TextView) inflater.inflate(R.layout.short_row_weather, null);
            shortWeather.setText(shortInfo.getDescription(i));
            shortWeather.setGravity(Gravity.CENTER);
            shortWeatherRow.addView(shortWeather);
            shortViewHolder.add(shortWeatherRow, shortWeather);

            TextView shortTemp = (TextView) inflater.inflate(R.layout.short_row_temp, null);
            shortTemp.setText(shortInfo.getTemperature(i));
            shortTemp.setGravity(Gravity.CENTER);
            shortTempRow.addView(shortTemp);
            shortViewHolder.add(shortTempRow, shortTemp);

            TextView shortRainRate = (TextView) inflater.inflate(R.layout.short_row_rainrate, null);
            shortRainRate.setText(shortInfo.getRainRate(i));
            shortRainRate.setGravity(Gravity.CENTER);
            shortRainRateRow.addView(shortRainRate);
            shortViewHolder.add(shortRainRateRow, shortRainRate);

            TextView shortRainFall = (TextView) inflater.inflate(R.layout.short_row_rainfall, null);
            shortRainFall.setText(shortInfo.getRainFall(i));
            shortRainFall.setGravity(Gravity.CENTER);
            shortRainFallRow.addView(shortRainFall);
            shortViewHolder.add(shortRainFallRow, shortRainFall);

            TextView shortWindSpeed = (TextView) inflater.inflate(R.layout.short_row_windspeed, null);
            shortWindSpeed.setText(shortInfo.getWindSpeed(i));
            shortWindSpeed.setGravity(Gravity.CENTER);
            shortWindSpeedRow.addView(shortWindSpeed);
            shortViewHolder.add(shortWindSpeedRow, shortWindSpeed);

            TextView shortHumidity = (TextView) inflater.inflate(R.layout.short_row_humidity, null);
            shortHumidity.setText(shortInfo.getHumidity(i));
            shortHumidity.setGravity(Gravity.CENTER);
            shortHumidityRow.addView(shortHumidity);
            shortViewHolder.add(shortHumidityRow, shortHumidity);
        }
    }

    /**
     * 주간 날씨 영역을 표시한다.
     */
    private void showWeeklyWeather() {
        weeklyLocationName = (TextView) findViewById(R.id.weekly_location_name);
        weeklyLocationName.setText(locationInfo.getLocation());
        weeklyTime = (TextView) findViewById(R.id.weekly_time);
        weeklyTime.setText(weeklyInfo.getForcastTimeString() + " "
                + getResources().getString(R.string.forcast));
        weeklyExplain = (TextView) findViewById(R.id.weekly_explain);
        weeklyExplain.setText(weeklyInfo.getExplain());

        LayoutInflater inflater = getLayoutInflater();

        weeklyDayRow = (TableRow) findViewById(R.id.weekly_day_row);
        weeklyDateRow = (TableRow) findViewById(R.id.weekly_date_row);
        weeklyWeatherImageRow = (TableRow) findViewById(R.id.weekly_weather_image_row);
        weeklyWeatherRow = (TableRow) findViewById(R.id.weekly_weather_row);
        weeklyTempMaxRow = (TableRow) findViewById(R.id.weekly_temp_max_row);
        weeklyTempMinRow = (TableRow) findViewById(R.id.weekly_temp_min_row);

        weeklyViewHolder.removeAll();

        int length = WeeklyInfo.LENGTH;
        for (int i = 0; i < length; i++) {
            TextView weeklyDay = (TextView) inflater.inflate(R.layout.weekly_row_day, null);
            weeklyDay.setText(weeklyInfo.getDayOfWeek(i));
            weeklyDay.setGravity(Gravity.CENTER);
            weeklyDayRow.addView(weeklyDay);
            weeklyViewHolder.add(weeklyDayRow, weeklyDay);

            TextView weeklyDate = (TextView) inflater.inflate(R.layout.weekly_row_date, null);
            weeklyDate.setText(weeklyInfo.getDate(i));
            weeklyDate.setGravity(Gravity.CENTER);
            weeklyDateRow.addView(weeklyDate);
            weeklyViewHolder.add(weeklyDateRow, weeklyDate);

            ImageView weeklyWeatherImage = (ImageView) inflater.inflate(R.layout.short_row_weather_image,
                    null);
            weeklyWeatherImage.setImageResource(getWeatherIcon(weeklyInfo.getWeatherCode(i)));
            weeklyWeatherImage.setMaxHeight(50);
            weeklyWeatherImage.setMaxWidth(50);
            weeklyWeatherImageRow.addView(weeklyWeatherImage);
            weeklyViewHolder.add(weeklyWeatherImageRow, weeklyWeatherImage);

            TextView weeklyWeather = (TextView) inflater.inflate(R.layout.weekly_row_weather, null);
            weeklyWeather.setText(weeklyInfo.getWeatherDescription(i));
            weeklyWeather.setGravity(Gravity.CENTER);
            weeklyWeatherRow.addView(weeklyWeather);
            weeklyViewHolder.add(weeklyWeatherRow, weeklyWeather);

            TextView weeklyTempMax = (TextView) inflater.inflate(R.layout.weekly_row_temp_max, null);
            weeklyTempMax.setText(weeklyInfo.getTemperatureMax(i));
            weeklyTempMax.setGravity(Gravity.CENTER);
            weeklyTempMaxRow.addView(weeklyTempMax);
            weeklyViewHolder.add(weeklyTempMaxRow, weeklyTempMax);

            TextView shortTempMin = (TextView) inflater.inflate(R.layout.weekly_row_temp_min, null);
            shortTempMin.setText(weeklyInfo.getTemperatureMin(i));
            shortTempMin.setGravity(Gravity.CENTER);
            weeklyTempMinRow.addView(shortTempMin);
            weeklyViewHolder.add(weeklyTempMinRow, shortTempMin);
        }

        weeklyTable = (TableLayout) findViewById(R.id.weekly_table);
        weeklyTable.setShrinkAllColumns(true);
    }

    /**
     * 위성 사진을 화면에 표시한다.
     */
    private void showSatImageInfo() {
        satImage = (ImageView) findViewById(R.id.sat_image);
        satImageTime = (TextView) findViewById(R.id.sat_image_time);
        satImage.setImageDrawable(satImageDrawable);
        satImageTime.setText(satImageTimeString);
    }

    /**
     * 기상정보를 수신한다.
     * 
     * @param locationId
     */
    private void receiveWeatherInfo(int locationId) {
        
        // 현재 기상정보 수신 중인지 확인
        if(progress != null) {
            return;
        }
        
        // 관심지역 기상 코드 조회
        locationInfo = QueryHelper.getLocationInfo(this, locationId);

        // 기상정보 수신하는 동안 ProgressDialog를 보여준다.
        progress = new ProgressDialog(this);
        progress.setMessage(getResources().getString(R.string.parsing_progress_message));
        progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progress.setTitle(null);
        progress.setMax(100);
        progress.setCancelable(false);
        progress.show();

        // 기상정보를 수신하는 백그라운드 스레드
        Thread parseWeatherInfo = new Thread(new Runnable() {
            public void run() {
                try {
                    currentInfo = parseCurrentWeather();
                    shortInfo = parseShortRangeWeather();
                    weeklyInfo = parseWeeklyWeather();
                    // 파싱 완료 통지
                    alreadyWeatherInfoReceived = true;
                    handler.sendMessage(handler.obtainMessage(MSG_PARSING_COMPLETED));
                } catch (Exception e) {
                    Log.e(TAG, "Parsing Error", e);
                    handler.sendMessage(handler.obtainMessage(MSG_PARSING_ERROR));
                }
            }
        });
        parseWeatherInfo.start();
    }

    /**
     * 위성 사진을 수신한다.
     */
    private void receiveSatPicture() {
        // 위성 사진을 수신하는 백그라운드 스레드
        Thread parseSatPictureInfo = new Thread(new Runnable() {
            public void run() {
                try {
                    parseSatImage();
                    // 파싱 완료 통지
                    handler.sendMessage(handler.obtainMessage(MSG_SAT_IMG_PARSING_COMPLETED));
                } catch (Exception e) {
                    Log.e(TAG, "Parsing Error", e);
                    handler.sendMessage(handler.obtainMessage(MSG_SAT_IMG_PARSING_ERROR));
                }
            }
        });
        parseSatPictureInfo.start();
    }

    /**
     * 
     */
    private void parseSatImage() throws Exception {
        String url = "http://m.kma.go.kr/m/image/image_02.jsp";

        HttpClient client = new DefaultHttpClient();
        HttpGet request = new HttpGet(url);
        HttpResponse response = client.execute(request);

        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            InputStream is = response.getEntity().getContent();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));

            StringBuffer sb = new StringBuffer();
            String tmp = null;
            while ((tmp = br.readLine()) != null) {
                sb.append(tmp);
            }

            is.close();
            // http://m.kma.go.kr/repositary/image/sat/coms/coms_mi_le1b_ir1_k_201106010215.thn.png
            int start = sb.indexOf("/repositary/image/sat/coms");
            int end = sb.indexOf(".png", start);
            String imageUrl = "http://m.kma.go.kr" + sb.substring(start, end + 4);
            String imageUtcTime = sb.substring(end - 16, end);
            satImageTimeString = Util.getLocalTimeOfSatImage(imageUtcTime);

            request = new HttpGet(imageUrl);
            response = client.execute(request);

            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                is = response.getEntity().getContent();
                BufferedInputStream in = new BufferedInputStream(is);

                satImageDrawable = Drawable.createFromStream(in, "none");
                is.close();
            } else {
                throw new Exception("위성 사진 다운로드 중 에러");
            }
        } else {
            throw new Exception("위성 사진 주소 파싱 중 에러");
        }

    }

    /**
     * 전국 현재 날씨에서 특정 지역 날씨만 파싱
     * 
     * @throws Exception
     */
    private CurrentInfo parseCurrentWeather() throws Exception {
        // 지역 코드
        String stnId = locationInfo.getNowStnId();

        HttpClient client = new DefaultHttpClient();
        HttpGet request = new HttpGet(locationInfo.getCurrentWeatherUrl());
        HttpResponse response = client.execute(request);

        CurrentInfo currentInfo = new CurrentInfo();

        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            InputStream is = response.getEntity().getContent();

            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = builder.parse(is);

            NodeList weather = doc.getElementsByTagName("weather");
            currentInfo.setYear(Util.getNodeAttributeValue(weather, "year", 0));
            currentInfo.setMonth(Util.getNodeAttributeValue(weather, "month", 0));
            currentInfo.setDay(Util.getNodeAttributeValue(weather, "day", 0));
            currentInfo.setHour(Util.getNodeAttributeValue(weather, "hour", 0));

            NodeList locals = doc.getElementsByTagName("local");
            int localsLen = locals.getLength();
            for (int i = 0; i < localsLen; i++) {
                Element local = (Element) locals.item(i);
                if (stnId.equals(local.getAttribute("stn_id"))) {
                    currentInfo.setIcon(Util.getNodeAttributeValue(locals, "icon", i));
                    currentInfo.setDescription(Util.getNodeAttributeValue(locals, "desc", i));
                    currentInfo.setTemperature(Util.getNodeAttributeValue(locals, "ta", i));
                    currentInfo.setRainFall(Util.getNodeAttributeValue(locals, "rn_hr1", i));
                    break;
                }
            }

            handler.sendMessage(handler.obtainMessage(MSG_PARSING_PROGRESS));
            is.close();
        } else {
            currentInfo.setOk(false);
        }

        return currentInfo;
    }

    /**
     * 단기예보(3일) 파싱
     */
    private ShortRangeInfo parseShortRangeWeather() throws Exception {
        ShortRangeInfo shortInfo = new ShortRangeInfo();

        HttpClient client = new DefaultHttpClient();

        // 단기예보(3일)
        HttpGet request = new HttpGet(locationInfo.getShortRangeUrl());
        HttpResponse response = client.execute(request);

        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            InputStream is = response.getEntity().getContent();

            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = builder.parse(is);

            NodeList tm = doc.getElementsByTagName("tm");
            Element forcastTime = (Element) tm.item(0);
            shortInfo.setForcastTime(forcastTime.getFirstChild().getNodeValue());

            NodeList data = doc.getElementsByTagName("data");
            NodeList hour = doc.getElementsByTagName("hour");
            NodeList day = doc.getElementsByTagName("day");
            NodeList temperature = doc.getElementsByTagName("temp");
            NodeList temperatureMin = doc.getElementsByTagName("tmn");
            NodeList temperatureMax = doc.getElementsByTagName("tmx");
            NodeList sky = doc.getElementsByTagName("sky");
            NodeList rainType = doc.getElementsByTagName("pty");
            NodeList description = doc.getElementsByTagName("wfKor");
            NodeList rainRate = doc.getElementsByTagName("pop");
            NodeList rainFall = doc.getElementsByTagName("r12");
            NodeList snowFall = doc.getElementsByTagName("s12");
            NodeList windSpeed = doc.getElementsByTagName("ws");
            NodeList windDirection = doc.getElementsByTagName("wd");
            NodeList windDirectionKor = doc.getElementsByTagName("wdKor");
            NodeList humidity = doc.getElementsByTagName("reh");

            int dataLen = data.getLength();
            for (int i = 0; i < dataLen; i++) {
                shortInfo.setDataSeq(i, Util.getNodeAttributeValue(data, "seq", i));
                shortInfo.setHour(i, Util.getNodeValue(hour, i));
                shortInfo.setDay(i, Util.getNodeValue(day, i));
                shortInfo.setTemperature(i, Util.getNodeValue(temperature, i));
                shortInfo.setTemperatureMin(i, Util.getNodeValue(temperatureMin, i));
                shortInfo.setTemperatureMax(i, Util.getNodeValue(temperatureMax, i));
                shortInfo.setSky(i, Util.getNodeValue(sky, i));
                shortInfo.setRainType(i, Util.getNodeValue(rainType, i));
                shortInfo.setDescription(i, Util.getNodeValue(description, i));
                shortInfo.setRainRate(i, Util.getNodeValue(rainRate, i));
                shortInfo.setRainFall(i, Util.getNodeValue(rainFall, i));
                shortInfo.setSnowFall(i, Util.getNodeValue(snowFall, i));
                shortInfo.setWindSpeed(i, Util.getNodeValue(windSpeed, i));
                shortInfo.setWindDirection(i, Util.getNodeValue(windDirection, i));
                shortInfo.setWindDirectionKor(i, Util.getNodeValue(windDirectionKor, i));
                shortInfo.setHumidity(i, Util.getNodeValue(humidity, i));
            }

            handler.sendMessage(handler.obtainMessage(MSG_PARSING_PROGRESS));
            is.close();
        } else {
            shortInfo.setOk(false);
        }

        return shortInfo;
    }

    /**
     * 주간예보 파싱(6일분)
     * 
     * @throws Exception
     */
    private WeeklyInfo parseWeeklyWeather() throws Exception {
        WeeklyInfo weeklyInfo = new WeeklyInfo();
        HttpClient client = new DefaultHttpClient();

        // 주간예보 - 전망
        HttpGet request = new HttpGet(locationInfo.getWeekExplainUrl());
        HttpResponse response = client.execute(request);

        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            InputStream is = response.getEntity().getContent();

            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = builder.parse(is);

            NodeList wf_sv = doc.getElementsByTagName("wf_sv");
            weeklyInfo.setExplain(Util.getNodeValue(wf_sv, 0));

            handler.sendMessage(handler.obtainMessage(MSG_PARSING_PROGRESS));
            is.close();
        } else {
            weeklyInfo.setOk(false);
        }

        // 주간예보 - 기온
        request = new HttpGet(locationInfo.getWeekTempUrl());
        response = client.execute(request);

        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            InputStream is = response.getEntity().getContent();

            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = builder.parse(is);

            NodeList tm_fc = doc.getElementsByTagName("tm_fc");
            weeklyInfo.setForcastTime(Util.getNodeValue(tm_fc, 0));

            NodeList tempMins = doc.getElementsByTagName("ta_min");
            NodeList tempMaxs = doc.getElementsByTagName("ta_max");
            int tempMinsLen = tempMins.getLength();
            for (int i = 0; i < tempMinsLen; i++) {
                weeklyInfo.setTemperatureMin(i, Util.getNodeValue(tempMins, i));
                weeklyInfo.setTemperatureMax(i, Util.getNodeValue(tempMaxs, i));
            }

            handler.sendMessage(handler.obtainMessage(MSG_PARSING_PROGRESS));
            is.close();
        } else {
            weeklyInfo.setOk(false);
        }

        // 주간예보 - 날씨
        request = new HttpGet(locationInfo.getWeekWeatherUrl());
        response = client.execute(request);

        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            InputStream is = response.getEntity().getContent();

            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = builder.parse(is);

            NodeList weatherDescs = doc.getElementsByTagName("wf");
            NodeList weatherCodes = doc.getElementsByTagName("wf_cd");
            int weatherDescsLen = weatherDescs.getLength();
            for (int i = 0; i < weatherDescsLen; i++) {
                weeklyInfo.setWeatherDescription(i, Util.getNodeValue(weatherDescs, i));
                weeklyInfo.setWeatherCode(i, Util.getNodeValue(weatherCodes, i));
            }

            handler.sendMessage(handler.obtainMessage(MSG_PARSING_PROGRESS));
            is.close();
        } else {
            weeklyInfo.setOk(false);
        }

        return weeklyInfo;
    }

    /**
     * 최초 실행시 DB 파일을 복사하는 작업을 수행한다.
     */
    public void copyDBFile() {
        String packagePath = "/data/data/" + getPackageName() + "/";
        String dbFileName = "weather.db";

        File dbFile = new File(packagePath + "databases/weather.db");
        if (dbFile.exists()) {
            return;
        }

        File folder = new File(packagePath + "databases");
        if (!folder.exists()) {
            folder.mkdirs();
        }

        File outfile = new File(packagePath + "databases/" + dbFileName);
        AssetManager assetManager = getResources().getAssets();
        BufferedInputStream in = null;
        BufferedOutputStream out = null;
        try {
            in = new BufferedInputStream(assetManager.open(dbFileName, AssetManager.ACCESS_BUFFER));
            out = new BufferedOutputStream(new FileOutputStream(outfile));
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }

            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e) {
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                }
            }
        }
    }

    /**
     * 날씨 아이콘 목록 정보를 생성한다.
     */
    private void initWeatherIconList() {
        weatherIconList = new HashMap<String, Integer>();
        weatherIconList.put("00", R.drawable.weather_icon_01);
        weatherIconList.put("02", R.drawable.weather_icon_02);
        weatherIconList.put("03", R.drawable.weather_icon_03);
        weatherIconList.put("04", R.drawable.weather_icon_04);
        weatherIconList.put("05", R.drawable.weather_icon_05);
        weatherIconList.put("07", R.drawable.weather_icon_07);
        weatherIconList.put("08", R.drawable.weather_icon_08);
        weatherIconList.put("11", R.drawable.weather_icon_11);
        weatherIconList.put("13", R.drawable.weather_icon_13);
        weatherIconList.put("14", R.drawable.weather_icon_14);
        weatherIconList.put("15", R.drawable.weather_icon_15);
    }

    /**
     * 코드에 맞는 날씨 아이콘을 반환한다.
     * 
     * @param iconCode
     * @return
     */
    private int getWeatherIcon(String iconCode) {
        Integer resId = null;
        if (iconCode != null && !iconCode.trim().equals("")) {
            resId = weatherIconList.get(iconCode);
        }

        if (resId == null) {
            // 코드에 해당하는 아이콘이 없으면 default 반환
            resId = weatherIconList.get("00");
        }

        return resId;
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // 화면 회전시 호출되는 메소드. 아무것도 수행하지 않는다.
        // 아무것도 수행하지 않으면, 현재의 layout이 회전된 화면에 맞게 줄거나 늘어나서 보여진다.
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "onPause()");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i(TAG, "onStop()");
        
        sensorManager.unregisterListener(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy()");
    }

    /**
     * Setup a new 3D rotation on the container view.
     * 
     * @param position
     *            the item that was clicked to show a picture, or -1 to show the
     *            list
     * @param start
     *            the start angle at which the rotation must begin
     * @param end
     *            the end angle of the rotation
     */
    private void applyRotation(int position, float start, float end) {
        // Find the center of the container
        final float centerX = frameLayout.getWidth() / 2.0f;
        final float centerY = frameLayout.getHeight() / 2.0f;

        // Create a new 3D rotation with the supplied parameter
        // The animation listener is used to trigger the next animation
        Rotate3dAnimation rotation = new Rotate3dAnimation(start, end, centerX, centerY, 310.0f, true);
        rotation.setDuration(ROTATION_DURATION);
        rotation.setFillAfter(true);
        rotation.setInterpolator(new AccelerateInterpolator());
        rotation.setAnimationListener(new DisplayNextView(position));

        frameLayout.startAnimation(rotation);
    }

    /**
     * This class listens for the end of the first half of the animation. It
     * then posts a new action that effectively swaps the views when the
     * container is rotated 90 degrees and thus invisible.
     */
    private final class DisplayNextView implements Animation.AnimationListener {
        private final int mPosition;

        private DisplayNextView(int position) {
            mPosition = position;
        }

        public void onAnimationStart(Animation animation) {
        }

        public void onAnimationEnd(Animation animation) {
            frameLayout.post(new SwapViews(mPosition));
        }

        public void onAnimationRepeat(Animation animation) {
        }
    }

    /**
     * This class is responsible for swapping the views and start the second
     * half of the animation.
     */
    private final class SwapViews implements Runnable {
        private final int mPosition;

        public SwapViews(int position) {
            mPosition = position;
        }

        public void run() {
            final float centerX = frameLayout.getWidth() / 2.0f;
            final float centerY = frameLayout.getHeight() / 2.0f;
            Rotate3dAnimation rotation = null;

            if (mPosition == 0) { // 오늘 클릭
                weeklyPage.setVisibility(View.GONE);
                satPage.setVisibility(View.GONE);
                todayPage.setVisibility(View.VISIBLE);
                todayPage.requestFocus();

                rotation = new Rotate3dAnimation(90, 0, centerX, centerY, 310.0f, false);
            } else if (mPosition == 1) { // 주간
                boolean forward = true;
                if (todayPage.getVisibility() == View.VISIBLE) {
                    forward = false;
                }

                todayPage.setVisibility(View.GONE);
                satPage.setVisibility(View.GONE);
                weeklyPage.setVisibility(View.VISIBLE);
                weeklyPage.requestFocus();

                // 원래 ApiDemos 소스
                // rotation = new Rotate3dAnimation(90, 180, centerX, centerY,
                // 310.0f, false);

                // 회전 방향 결정(가운데 화면만 판단하면 됨)
                if (forward) {
                    rotation = new Rotate3dAnimation(90, 0, centerX, centerY, 310.0f, false);
                } else {
                    rotation = new Rotate3dAnimation(270, 360, centerX, centerY, 310.0f, false);
                }
            } else if (mPosition == 2) { // 위성
                todayPage.setVisibility(View.GONE);
                weeklyPage.setVisibility(View.GONE);
                satPage.setVisibility(View.VISIBLE);
                satPage.requestFocus();

                // 원래 ApiDemos 소스
                // rotation = new Rotate3dAnimation(90, 180, centerX, centerY,
                // 310.0f, false);
                rotation = new Rotate3dAnimation(270, 360, centerX, centerY, 310.0f, false);
            }

            rotation.setDuration(ROTATION_DURATION);
            rotation.setFillAfter(true);
            rotation.setInterpolator(new DecelerateInterpolator());

            frameLayout.startAnimation(rotation);
        }
    }
}